<?php

final class sys extends Singletone
{
    static protected $libs_dirname = 'lib-n-tools';
    static protected $libs_extension = '.php';
    static protected $plugins_dirname = 'plug-ins';
    static protected $plugins_extension = '.php';
    static protected $view_extension = '.ctp';
    static protected $actions_dirname = 'events-reactors';
    static protected $active_events = array();

    static protected $libs = array();
    static protected $plugins = array();

    static
    function
    import($path)
    {
        try 
        {  
            FS::checkPath($path);
            require_once $path;
        }
        
        catch (TheException $e) 
        {
            throw new ArchException('Can’t load code-file. ' . $e->getMessage());
        }               
    }
    
    static 
    function 
    uses()
    { $ARGs = func_get_args();
        
        foreach ($ARGs as $path) 
        {
            $path = FS::undPath($path);
            
            if (isset(self::$libs[$path])) {
                
                /* Already imported */
                continue;
            }
            
            /* Registration in the System */
            self::import(  Application::enginePath() . self::$libs_dirname .I
                              //            
                              . $path . self::$libs_extension  ); 
            
            self::$libs[$path] = true;
            
        }
    }

    static 
    function 
    addPlugin($path)
    {
        /**
         * Загружать плагины можно только из следующих контекстов:
         * — Файл инициализации приложения.
         *   Т. е. тот файл, который непосредственно подключает файл инициализации фреймворка.
         * — Файл загрузки фреймворка boot.php.
         * — Файл загрузки приложения (Config::get("boot_file")).
         * — Обработчик события PageLoad.
         */

        if (isset(self::$plugins[$path])) {
            return;
        }
        $file_path = str_replace('/', DIRECTORY_SEPARATOR, $path);

        $file_name = basename($file_path);
        $class_name = $file_name;

        $file_path = Application::rootPath().self::$plugins_dirname.DIRECTORY_SEPARATOR.$file_name.self::$plugins_extension;
        if (file_exists($file_path) && is_file($file_path) && is_readable($file_path)) {
        } else {
            $file_path = Application::enginePath().self::$plugins_dirname.DIRECTORY_SEPARATOR.$file_name.self::$plugins_extension;
            if (file_exists($file_path) && is_file($file_path) && is_readable($file_path)) {
            } else {
                throw new ArchException("Can’t load plugin “{$path}”.");
            }
        }

        require_once $file_path;
        if (! class_exists($class_name) || ! in_array("AbstractPlugin", get_classes("OutputEncode"))) {
            throw new ArchException("Can’t load plugin “{$path}”.");
        }

        self::$plugins[$path] = call_user_func(array($class_name, "getInstance"));
    }

    static protected function getActiveEvents()
    {
        return self::$active_events;
    }

    static protected function makeEvent(Event $event)
    {
        self::$active_events[$event->name] = true;

        foreach (self::$plugins as $name => $obj) {
            /* @var $obj AbstractPlugin */
            $obj->AcceptEvent(clone $event);
        }

        unset(self::$active_events[$event->name]);
    }

    static function render($view_file, $data = null)
    {
        $view = new view();

        self::headerQuiet("Cache-Control: no-store, no-cache, must-revalidate");
        self::headerQuiet("Expires: 0");
        self::headerQuiet("Pragma: no-cache");

        $view->render($view_file, $data);
    }

    static function output($data = null, $contentType = 'application/octet-stream')
    {
        self::headerQuiet("Cache-Control: no-store, no-cache, must-revalidate");
        self::headerQuiet("Expires: 0");
        self::headerQuiet("Pragma: no-cache");

        echo $data;
    }

    static function outfile($data = null, $contentType = 'application/octet-stream', $compressor = 'inline')
    {
        self::headerQuiet("Cache-Control: no-store, no-cache, must-revalidate");
        self::headerQuiet("Expires: 0");
        self::headerQuiet("Pragma: no-cache");
        self::headerQuiet("Transfer-Encoding: ".$compressor);
        self::headerQuiet("Content-Type: ".$contentType);

        echo $data;
    }

    /**
     * Возвращает объект плагина.
     *
     * @param string $path
     * @return AbstractPlugin
     */
    static function getPlugin($path)
    {
        if (! isset(self::$plugins[$path])) {
            throw new ArchException();
        }

        return self::$plugins[$path];
    }

    /**
     * Вывод содержимого переменной в поток.
     *
     * @param mixxed $var
     */
    static function vardump($var, $overload = 0)
    {
        if (! Config::get("debug")) {
            return;
        }
        $stack = debug_backtrace();
        $stack_index = $overload ? 1 + $overload : 0;
        $stack = isset($stack[$stack_index]) ? $stack[$stack_index] : array();
        echo "<div>";
        if (isset($stack["file"]) && isset($stack["line"])) {
            echo "<strong>",
                str_replace(Application::rootPath(), "", $stack['file']),
            "</strong> (line <strong>{$stack['line']}</strong>)";
        }
        echo "<pre>",
            htmlspecialchars(print_r($var, true)),
        "</pre>";
        echo "</div>";
    }

    /**
     * Обработчик события завершения выполнения скрипта.
     */
    static function onShutdown()
    {
        self::makeEvent(new Event("Shutdown"));
    }


    /**
     * Обработчик события окончания инициализации.
     */
    static function onInit()
    {
        $stack = debug_backtrace();
        $file = isset($stack[0]['file']) ? $stack[0]['file'] : "";

        if ($file !== Application::enginePath()."framework.php") 
        {
            throw new ArchException();
        }

        self::makeEvent(new Event("Init"));
    }

    /**
     * Переопределение стандартной функции header().
     * Данная функция при неудачной отправке генерирует исключение ArchException.
     *
     * @param string $header
     * @param bool[optional] $replace
     * @param int[optional] $http_response_code
     */
    static function header($header, $replace = null, $http_response_code = null)
    {
        $args = func_get_args();
        if (call_user_func_array(__CLASS__.'::headerQuiet', $args) === false) 
        {
            throw new ArchException('Cannot modify header information - headers already sent');
        }
    }

    /**
     * Переопределение стандартной функции header().
     * Данная функция при неудачной отправке возвращает false.
     *
     * @param string $header
     * @param bool[optional] $replace
     * @param int[optional] $http_response_code
     */
    static function headerQuiet($header, $replace = null, $http_response_code = null)
    {
        static $wasError = false;
        $args = func_get_args();
        
        /**
         * Функция перегружена: 
         *   -- сигнатура (int, str,  str,  str [...]) считается как колбек обработчика ошибок
         *   -- любая другая -- прямой вызов из программного кода. 
		 */
        if (
	        (count($args) >= 4)
	        
	        &&  is_int($args[0])   // $errno 
	        && is_string($args[1]) // $errstr 
	        && is_string($args[2]) // $errfile 
	        &&   is_int($args[3])  // $errline
	            )
        {
            if ($args[2] === __FILE__) 
            {
            	
            	// Вызвана ошибка в этом файле
            	// Скорее всего как раз врезультате нашего переопределения
            	$wasError = true;
            }
        }
        
        else
        {
	
	        /**
	         * Делегируем вызов родной PHP-функции.
	         */
	        set_error_handler(array(__CLASS__, __FUNCTION__));
	        $wasError = false;
	        call_user_func_array('header', $args);
	        restore_error_handler();
            if ($wasError)
            {
            	return false;
            }
        }
    }
}

class Event extends AbstractObject
{
    public $_name;
    public $_cancellable;

    function __construct($name, $cancellable = false)
    {
        parent::__construct();
        $this->__readonly_fields[] = "name";
        $this->__readonly_fields[] = "cancellable";

        $this->_name = $name;
        $this->_cancellable = $cancellable;
    }
}

/**
 * Псевдоним для sys::vardump().
 */
function debug($var)
{
    $args = func_get_args();
    $args[1] = 1;
    $result = call_user_func_array('sys::vardump', $args);
    return $result;
}

function get_classes($obj, $top=null)
{
    $result = array();
    $class = null;
    if (is_object($obj)) {
        $class = get_class($obj);
    } elseif (is_string($obj)) {
        if (class_exists($obj)) {
            $class = $obj;
        }
    }
    if (isset($class)) {
        $result[] = $class;
        while ($parent = get_parent_class($class)) {
            $result[] = $class = $parent;
            if (!is_null($top) && ($class === $top)) {
                break;
            }
        }
    }

    return $result;
}

?>